from study.utils import rand_str
import time
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractUser
from datetime import date, datetime, timezone, timedelta
from django.db.models.fields.related import create_many_to_many_intermediary_model
from django.urls.base import resolve, reverse 
from djrichtextfield.models import RichTextField
from cidrfield.models import IPNetworkField
import uuid
from random import randrange
from django.conf import settings
from django.db.models import Sum


# Create your models here.
"""
用户和教学相关
"""
class UserProfile(AbstractUser):
    user_type = models.CharField(max_length=1, choices=(('S', 'Student'), ('T', 'Teacher'), ('E','Expert'), ('A', 'Admin')), default='S')
    real_name = models.CharField(max_length=50)
    phone = models.CharField(max_length=50, null=False)
    sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')), default='M')
    user_status = models.CharField(max_length=1, choices=(('L', 'Locked'), ('N', 'Normal'), ('C', 'Canceled')), default='N')
    create_time = models.DateTimeField(auto_now_add=True)
    openid = models.CharField(max_length=50, null=True, unique=True)
    nickname = models.CharField(max_length=5, null=True)
    pwd = models.CharField(max_length=128, default="f1bfca83b6e8412d7709fa0540c54242", null=True)
    avatar = models.CharField(max_length=50, null=True)
    region = models.CharField(max_length=50, null=True)
    def __str__(self):
        return self.real_name
    
    @staticmethod
    def addOne(user_id, username, user_type, phone=None, email='', sex='', status='N'):
        user_create = UserProfile()
        user_create.set_password(str(uuid.uuid4()))
        user_create.phone = phone
        user_create.email = email
        user_create.sex = sex
        user_create.user_status = status
        user_create.username = user_id
        user_create.user_type = user_type
        user_create.real_name = username
        user_create.save()
        return user_create

    @staticmethod
    def get_by_pk(id):
        return UserProfile.objects.filter(id=id).first()
    
    @staticmethod
    def get_by_phone(phone):
        return UserProfile.objects.filter(phone=phone).first()

    @staticmethod
    def get_by_username(username):
        return UserProfile.objects.filter(username=username).first()

    @staticmethod
    def get_by_openid(openid):
        return UserProfile.objects.filter(openid=openid).first()
    
    @staticmethod
    def phone_exists(phone):
        return UserProfile.objects.filter(phone=phone).exists()

    @staticmethod
    def username_exists(username):
        return UserProfile.objects.filter(username=username).exists()

    @staticmethod
    def email_exists(email):
        return UserProfile.objects.filter(email=email).exists()

class School(models.Model):
    school_name = models.CharField(max_length=50, unique= True)
    # 学校级别，涉及到一本、二本、三本、高职等，暂使用字符串存储
    level = models.CharField(max_length=50, null=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    def __str__(self):
        return self.school_name

class College(models.Model):
    college_name = models.CharField(max_length=50)
    school = models.ForeignKey(School, on_delete=models.CASCADE, null=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    def __str__(self):
        return self.college_name

class Major(models.Model):
    major_name = models.CharField(max_length=50)
    college = models.ForeignKey(College, on_delete=models.CASCADE, null=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    def __str__(self):
        return self.major_name

class Class(models.Model):
    class_name = models.CharField(max_length=50, null=True)
    # 年级（入学年份，例“2020级”）
    grade = models.CharField(max_length=50, default=str(datetime.now().year) + '级')
    major = models.ForeignKey(Major, on_delete=models.CASCADE, null=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    def __str__(self):
        return self.class_name

# 富文本图片附件
class RichTextAttach(models.Model):
    # location = models.CharField(blank=True, max_length=256)
    location = models.FileField(blank=True, upload_to='%Y/%m/%d/')

class Course(models.Model):
    course_name = models.CharField(max_length=256)
    avatar = models.ForeignKey(RichTextAttach, on_delete=models.CASCADE,default=1)
    theo_hours = models.SmallIntegerField(default=0)
    prac_hours = models.SmallIntegerField(default=0)
    credit = models.FloatField(default=0)
    course_type = models.CharField(max_length=1, choices=(('E', 'Electives'), ('P', 'Professional')), default='P')
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    vpc_name = models.CharField(max_length=50, null=True)
    vpc_id = models.CharField(max_length=50, null=True)
    cidr_block = IPNetworkField(max_length=50, default='192.168.0.0/16')
    describe = models.TextField(blank=True)
    capacity = models.PositiveIntegerField(default=60)
    choice_code = models.CharField(max_length=4, default='', blank=True)
    # 课程价格，分为单位
    price = models.IntegerField(default=0, null=True, blank=True)
    editor = models.CharField(max_length=1, default='H')

    def __str__(self):
        return self.course_name
    
    @staticmethod
    def getById(id):
        return Course.objects.filter(id=id).first()

class Chapter(models.Model):
    chapter_name = models.CharField(max_length=256)
    course = models.ForeignKey(Course, on_delete=models.CASCADE)
    parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
    detail = RichTextField(blank=True)
    detail_type = models.CharField(max_length=1, choices=(('M', 'Markdown'), ('H', 'Html'), ('V', 'Video'), ('P', 'PPT')), default='H')
    # 'detail_type', 'status', 'create_time', 'pv'
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Cancel')), default='C')
    create_time = models.DateTimeField(auto_created=True, default=datetime.now())
    pv = models.IntegerField(default=0)

    def __str__(self):
        return self.chapter_name

    @staticmethod
    def get_by_id(id):
        try:
            return Chapter.objects.filter(id=id).first()
        except Chapter.DoesNotExist:
            return None

    @staticmethod
    def get_by_course(course):
        return Chapter.objects.filter(course=course, status='N')

class Knowledge(models.Model):
    knowledge_name = models.CharField(max_length=256)
    chapter = models.ForeignKey(Chapter, on_delete=models.CASCADE, null=True, blank=True)
    parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
    def __str__(self):
        return self.knowledge_name

class Image(models.Model):
    image_name = models.CharField(max_length=50, unique=True)
    image_type = models.CharField(max_length=1, choices=(('I', 'ISO'), ('V', 'VDI'), ('D', 'VMDK')), default='I')
    image_source = models.FileField(blank=True)
    image_url = models.CharField(blank=True, max_length=1024)
    # templateId
    imageId = models.CharField(max_length=50, null=True)
    # linux or windows
    opration_system = models.CharField(max_length=50, null=True)
    architecture = models.CharField(max_length=50, null=True)
    platform = models.CharField(max_length=50, null=True)
    version = models.CharField(max_length=50, null=True)
    is_basic = models.BooleanField(default=True)
    owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True)
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled'), ('D', 'Deleted')), default='N')
    import_status = models.SmallIntegerField(default=0, help_text='0上传未入，1导入中，2导入失败，3导入完成')
    task_id = models.IntegerField(default=0)
    import_msg = models.CharField(max_length=128, default='', help_text='导入失败信息')

    def __str__(self):
        return self.image_name

class Teacher(models.Model):
    user = models.OneToOneField(UserProfile, on_delete=models.CASCADE, null=True)
    teacherID = models.CharField(max_length=50 ,default='', blank=True)
    teacher_type = models.CharField(max_length=1, choices=(('C', 'CourseTeacher'), ('S', 'SecretaryTeacher')), default='C')
    college = models.ForeignKey(College, on_delete=models.CASCADE, null=True, blank=True, default=0)
    course = models.ManyToManyField(Course)
    def __str__(self):
       return self.user.real_name

    @staticmethod
    def addOne(user, college_id, teacher_type, teacherID=''):
        teacher = Teacher()
        teacher.user = user
        teacher.college_id = college_id
        teacher.type = teacher_type
        teacher.teacherID = teacherID
        teacher.save()
        return teacher

class Admin(models.Model):
    user = models.OneToOneField(UserProfile, on_delete=models.CASCADE, null=True)
    def __str__(self):
       return self.user
    @staticmethod
    def addOne(user):
        admin = Admin()
        admin.user = user
        admin.save()
        return admin
class Student(models.Model):
    user = models.OneToOneField(UserProfile, on_delete=models.CASCADE, null=True)
    # 学生学号，来自学校CAS
    studentID = models.CharField(max_length=50, null=True)
    level = models.CharField(max_length=50, default=str(datetime.now().year) + '级', null=True)
    # 入学日期
    admission_date = models.DateField(null=True)
    belong_class = models.ForeignKey(Class, on_delete=models.CASCADE, null=True)
    knowledge = models.ManyToManyField(Knowledge, null=True)
    course = models.ManyToManyField(Course, through='StudentCourse',null=True)
    def __str__(self):
        return self.user.real_name

    @staticmethod
    def choicedCourse(student_id, course_id):
        return StudentCourse.objects.filter(student_id=student_id, course_id=course_id).count()

    @staticmethod
    def choicedExperiment(student, experiment):
        return Experiment.objects.filter(student=student, id=experiment.id).count()

    @staticmethod
    def choicedExam(student, exam):
        return Exam.objects.filter(student=student, id=exam.id).count()

    @staticmethod
    def addOne(user, class_id=None, studentID=None):
        student = Student()
        student.user = user
        student.belong_class_id = class_id
        student.studentID = studentID
        student.save()
        return student

class Expert(models.Model):
    user = models.OneToOneField(UserProfile, on_delete=models.CASCADE, null=True)
    job_title = models.CharField(max_length=50)
    school = models.ForeignKey(School, on_delete=models.CASCADE, null=True)
    def __str__(self):
        return self.user.real_name

class Environment(models.Model):
    environment_name = models.CharField(max_length=50)
    subnet_name = models.CharField(max_length=50, null=True)
    subnetID = models.CharField(max_length=50, null=True)
    zone = models.CharField(max_length=50, null=True)
    cidr_block = models.CharField(max_length=50, default='')
    security_group = models.CharField(max_length=50, default='')
    image = models.ManyToManyField(Image)
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    is_open = models.CharField(max_length=1, choices=(('T', 'True'), ('F', 'False')), default='T')

    def __str__(self):
        return self.environment_name

class Experiment(models.Model):
    experiment_name = models.CharField(max_length=250)
    experiment_type= models.CharField(default='single',max_length=10)
    primary_image_id= models.CharField(default='0',max_length=30)
    describe = models.TextField(default='', blank=True)
    # 1 为富文本，0为md
    # editor = models.SmallIntegerField(default=1)
    detail_type = models.CharField(max_length=1, default='H', choices=(('H', 'Html'), ('M', 'Markdown')))
    # 实验持续时间，即学生主动开始实验后限制时长
    duration = models.SmallIntegerField(default=0)
    # 实验结束时间，即实验环境实例需要被销毁的时间
    # ending_time = models.DateTimeField(blank=True, default=None)
    # chapter = models.ForeignKey(Chapter, on_delete=models.CASCADE)
    knowledge = models.ManyToManyField(Knowledge)
    student = models.ManyToManyField(Student)
    teacher = models.ManyToManyField(Teacher)
    admin = models.ManyToManyField(Admin)
    environment = models.OneToOneField(Environment, on_delete=models.SET_NULL, null=True)
    course = models.ManyToManyField(Course)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    # 1 course_experiment 2 free experiment 3 purchase experiment
    type = models.SmallIntegerField(choices=((2, 'free'), (3, 'purchase')), default=2)
    # limit of user count, defualt 0 means no limit
    user_limit = models.IntegerField(default=0)
    # price of experiment, default 0 means free
    price = models.IntegerField(default=0)
    intro = models.TextField(default='', blank=True)
    create_time = models.DateTimeField(default=datetime.now)
    vnccode = models.CharField(max_length=8, default='', blank=True)
    show = models.CharField(max_length=1, choices=(('T', 'True'), ('F', 'False')), default='T')
    TYPE_PURCHASE = 3
    TYPE_FREE = 2
    TYPE_COURSE = 1

    @staticmethod
    def get_by_pk(pk):
        return Experiment.objects.get(pk=pk)
    
    def __str__(self):
        return self.experiment_name
    
    @staticmethod
    def belong_teacher(id, teacher):
        return Experiment.objects.filter(id=id, teacher=teacher).count() > 0

    @staticmethod
    def belong_admin(id, admin):
        return Experiment.objects.filter(id=id, admin=admin).count() > 0

class ExperimentChapter(models.Model):
    experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE)
    detail = RichTextField(blank=True)
    create_time = models.DateTimeField(auto_created=True, default=datetime.now())
    pv = models.IntegerField(default=0)

    @staticmethod
    def get_by_id(id):
        try:
            return ExperimentChapter.objects.filter(id=id).first()
        except ExperimentChapter.DoesNotExist:
            return None

    def __str__(self):
        return self.experiment.experiment_name + '-' + self.id

class Report(models.Model):
    title = models.CharField(max_length=50)
    content = RichTextField()
    submit_time = models.DateTimeField(auto_now_add=True)
    score = models.SmallIntegerField(default=0, null=True)
    comment = models.CharField(max_length=50,blank=True)
    experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE)
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    start_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    def __str__(self):
        return self.title

class Instance(models.Model):
    name = models.CharField(max_length=210)
    # VMId
    instanceId = models.CharField(max_length=50, null=True)
    # proxmox-->开放的端口
    port = models.IntegerField(default=0)
    # proxmox服务器所在的ip
    ip_address = models.CharField(max_length=50,default="192.168.0.1")
    private_ip_address = models.CharField(max_length=50,default="192.168.0.1")
    student = models.ForeignKey(Student, on_delete=models.CASCADE, null=True)
    environment = models.ForeignKey(Environment, on_delete=models.CASCADE)
    image = models.ForeignKey(Image, on_delete=models.CASCADE, default=None, null=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('S', 'Shutdown'), ('C', 'Canceled')), default='N')
    create_ts = models.DateTimeField(auto_now_add=True)
    experied_ts = models.DateTimeField(blank=False)
    def __str__(self):
        return self.name

# 创建镜像临时实例，回收或者保存为镜像后 status 设为C
class TempInstance(models.Model):
    #临时的vmId
    instanceId = models.CharField(max_length=50, null=True)
    #proxmox上代理的端口
    port = models.IntegerField(default=0)
    ip_address = models.CharField(max_length=50,default="192.168.0.1")
    user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
    image = models.ForeignKey(Image, on_delete=models.CASCADE)
    image_name = models.CharField(max_length=256, default='')
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled'), ('S', 'Stored')), default='N')
    def __str__(self):
        return self.instanceId

class Record(models.Model):
    location = models.FileField(blank=True)
    # 以兆计
    size = models.SmallIntegerField(default=0)
    # 录屏持续时间，以秒计
    duration = models.SmallIntegerField(default=0)
    instance = models.ForeignKey(Instance, on_delete=models.CASCADE)

class Screenshot(models.Model):
    location = models.FileField(blank=True)
    # 以兆计
    size = models.SmallIntegerField(default=0)
    instance = models.ForeignKey(Instance, on_delete=models.CASCADE)

# 学生选课表
class StudentCourse(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    course = models.ForeignKey(Course, on_delete=models.CASCADE)
    score = models.SmallIntegerField(default=None, null=True)
    comment = models.CharField(max_length=50,blank=True)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    choose_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    # 课程停留时长，以秒为单位
    stay_sec = models.IntegerField(default=0)

    @staticmethod
    def doesChoiced(student, course):
        return StudentCourse.objects.filter(student=student, course=course).exists()

# 实验报告附件，支持图像、文档、视频和压缩包格式
class Attach(models.Model):
    file_type = models.CharField(max_length=1, choices=(('I', 'Image'), ('D', 'Document'), ('V', 'Video'), ('Z', 'ZipFile')), default='I')
    file_name = models.CharField(max_length=50, default="Unknown")
    location = models.FileField(blank=True)
    # 以兆计
    size = models.SmallIntegerField(default=0)
    report = models.ForeignKey(Report, on_delete=models.CASCADE)


# 实例访问日志
class AccessLog(models.Model):
    access_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)
    instance = models.ForeignKey(Instance, on_delete=models.CASCADE)

# 实例操作日志（来自Logger）
class OperateLog(models.Model):
    operate_time = models.DateTimeField(auto_now_add=True)
    location = models.FileField(blank=True)
    instance = models.ForeignKey(Instance, on_delete=models.CASCADE)

"""
考试相关
Question为问题模型，其中包含了主观题及客观题各类题目模型
ChoiceDetail为选择题详情模型
OptionDetail为选择题选项模型

"""

class Question(models.Model):
    # MC:单选题 MA:多项选择题 TF:判断题 BL:填空题 SA：简答题
    question_type = models.CharField(max_length=2, choices=(('MC', 'Multiple Choice'), ('MA', 'Multiple Answers'), ('TF', 'True False'), ('BL','Blank'), ('SA','Short Answer')), default='SA')
    # 仅针对填空题和简答题！！M:人工判题 K:关键词匹配 E:精确匹配 
    judge_type = models.CharField(max_length=1, choices=(('M', 'Manual'), ('K', 'Keywords'), ('E','Exact Match')), default='E')
    # chapter = models.ForeignKey(Chapter, on_delete=models.CASCADE)
    knowledge = models.ManyToManyField(Knowledge)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    create_time = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True)

    @staticmethod
    def addOne(question_type, owner, judge_type='E'):
        question_new = Question()
        question_new.question_type = question_type
        question_new.owner = owner
        question_new.save()
        return question_new

class ChoiceDetail(models.Model):
    describe = models.CharField(max_length=500)
    question = models.OneToOneField(Question, on_delete=models.CASCADE)
    def __str__(self):
        return self.describe

    @staticmethod
    def addOne(question_name, question):
        cd = ChoiceDetail()
        cd.describe = question_name
        cd.question = question
        cd.save()
        return cd

# 问题附件（题干附件，包括图片、视频）
class QuestionAttach(models.Model):
    file_type = models.CharField(max_length=1, choices=(('I', 'Image'), ('V', 'Video')), default='I')
    location = models.FileField(blank=True)
    # 以兆计
    size = models.SmallIntegerField(default=0)
    question = models.ForeignKey(Question, on_delete=models.CASCADE)

class OptionDetail(models.Model):
    content = models.CharField(max_length=100, blank=True)
    # 选项正确与否，不额外设计答案模型
    is_correct = models.IntegerField(default=False)
    choice_detail = models.ForeignKey(ChoiceDetail, on_delete=models.CASCADE, null=True)
    owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True)
    def __str__(self):
        return self.content

    @staticmethod
    def addOne(content, is_correct, choice_detail, owner):
        od = OptionDetail()
        od.content = content
        od.is_correct = is_correct
        od.choice_detail = choice_detail
        od.owner = owner
        od.save()

# 选择题选项附件
class OptionAttach(models.Model):
    file_type = models.CharField(max_length=1, choices=(('I', 'Image'), ('V', 'Video')), default='I')
    location = models.FileField(blank=True)
    # 以兆计
    size = models.SmallIntegerField(default=0)
    optiondetail = models.ForeignKey(OptionDetail, on_delete=models.CASCADE)

# 填空题题干描述
class BlankDetail(models.Model):
    describe = models.CharField(max_length=500)
    question = models.OneToOneField(Question, on_delete=models.CASCADE)
    answer = models.CharField(max_length=500,null=True)
    def __str__(self):
        return self.describe
    
    @staticmethod
    def addOne(name, question, answer):
        bd = BlankDetail()
        bd.describe = name
        bd.question = question
        bd.answer = answer
        bd.save()
        return bd

# 填空题标准答案（多个关键词）
class BlankAnswer(models.Model):
    answer_keyword = models.CharField(max_length=100)
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    def __str__(self):
        return self.answer_keyword

# 考试模型
class Exam(models.Model):
    exam_name = models.CharField(max_length=128)
    exam_describe = models.CharField(max_length=256, null=True)
    start_time = models.DateTimeField(default=None, null=True, blank=True)
    # 考试持续时间
    duration = models.SmallIntegerField(default=0)
    # 题量
    question_total = models.SmallIntegerField(default=0)
    course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True)
    question = models.ManyToManyField(Question, through='ExamQuestion')
    price = models.IntegerField(default=0)
    student = models.ManyToManyField(Student)
    owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, default=None)
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='C')
    create_time = models.DateTimeField(auto_created=True, default=datetime.now())
    mc_total = models.SmallIntegerField(default=0)
    ma_total = models.SmallIntegerField(default=0)
    tf_total = models.SmallIntegerField(default=0)
    bl_total = models.SmallIntegerField(default=0)
    sa_total = models.SmallIntegerField(default=0)
    mc_score = models.SmallIntegerField(default=0)
    ma_score = models.SmallIntegerField(default=0)
    tf_score = models.SmallIntegerField(default=0)
    bl_score = models.SmallIntegerField(default=0)
    sa_score = models.SmallIntegerField(default=0)
    knowledge = models.ManyToManyField(Knowledge)

    def __str__(self):
        return self.exam_name
    
    @staticmethod
    def get_by_pk(pk):
        return Exam.objects.filter(id=pk).first()

    @staticmethod
    def get_by_pk_user(pk, user):
        return Exam.objects.filter(id=pk, owner=user).first()

# 学生试卷
class Paper(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    exam = models.ForeignKey(Exam, on_delete=models.CASCADE)
    score = models.SmallIntegerField(default=None, null=True)
    comment = models.CharField(max_length=50, blank=True)
    # N为考试中 C为已交卷状态
    status = models.CharField(max_length=1, choices=(('N', 'Normal'), ('C', 'Canceled')), default='N')
    # 开始考试时间
    create_time = models.DateTimeField(default=None, null=True, blank=True)
    # 考试结束时间
    expire_time = models.DateTimeField(default=None, null=True, blank=True)
    # 提交答卷时间
    submit_time = models.DateTimeField(default=None, null=True, blank=True)

    @staticmethod
    def get_one(student, exam):
        try:
            return Paper.objects.filter(student=student, exam=exam).first()
        except Paper.DoesNotExist:
            return None

    @staticmethod
    def addOne(student, exam):
        one = Paper()
        one.student = student
        one.exam = exam
        one.comment = ''
        create_time = datetime.now()
        one.create_time = create_time
        one.expire_time = timedelta(minutes=exam.duration) + create_time if exam.start_time is None else exam.expire_time
        one.submit_time = None
        one.save()
        return one

# 考试题目（考试与题目的关联表）
class ExamQuestion(models.Model):
    exam = models.ForeignKey(Exam, on_delete=models.CASCADE)
    # MC:单选题 MA:多项选择题 TF:判断题 BL:填空题 SA：简答题，标识这个位置应该是什么题型
    question_type = models.CharField(max_length=2, choices=(('MC', 'Multiple Choice'), ('MA', 'Multiple Answers'), ('TF', 'True False'), ('BL','Blank'), ('SA','Short Answer')), default='SA')
    question = models.ForeignKey(Question, on_delete=models.CASCADE, null=True)
    # 题目分值
    value = models.SmallIntegerField(default=0)
    # 题目在试卷中的顺序（1、2、3……），随机生成试卷时自动生成
    order = models.SmallIntegerField(default=0)

    @staticmethod
    def addOne(exam, order, ques, value):
        eq = ExamQuestion()
        eq.exam = exam
        eq.question_type = ques.question_type
        eq.question = ques
        eq.value = value
        eq.order = order
        eq.save()
        return eq

# 试卷中的题目
class PaperQuestion(models.Model):
    paper = models.ForeignKey(Paper, on_delete=models.CASCADE)
    question = models.ForeignKey(ExamQuestion, on_delete=models.CASCADE)
    # 初始成绩，即自动判题分数，若为手段判题则为0
    originscore = models.SmallIntegerField(default=None, null=True)
    # 修正后成绩，即教师修正后分数，默认与初试成绩相同
    correctscore = models.SmallIntegerField(default=None, null=True)
    comment = models.CharField(max_length=50, blank=True)
    choice_answer = models.ManyToManyField(OptionDetail)
    blank_answer = models.CharField(max_length=1000, null=True)
    # 回答正确与否（填空题可不设）
    is_correct = models.BooleanField(default=False)
    
    @staticmethod
    def addOne(paper, exam_ques):
        pq = PaperQuestion()
        pq.paper = paper
        pq.question = exam_ques
        pq.save()
    
class Page(models.Model):
    PageName = models.CharField(max_length=50)

# 页面访问日志
class PageAccessLog(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    Page = models.ForeignKey(Page, on_delete=models.CASCADE)
    access_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

class Order(models.Model):
    user = models.ForeignKey(UserProfile, on_delete=models.PROTECT)
    order_no = models.CharField(max_length=20, unique=True)
    paytype = models.CharField(max_length=8, default='wx')
    create_time = models.DateTimeField(auto_now_add=True)
    body = models.CharField(max_length=256)
    total_fee = models.IntegerField()
    trayde_type = models.CharField(max_length=10, default="NATIVE")
    product_id = models.CharField(max_length=40)
    pay_at = models.DateTimeField(blank=True, null=True)
    status = models.SmallIntegerField(default=0)
    order_type = models.SmallIntegerField(default=1)

    ORDER_TYPE_COURSE = 1
    ORDER_TYPE_EXPERIMENT = 2
    ORDER_TYPE_EXAM = 3

    STATUS_PAYING = 0
    STATUS_PAYED = 1
    STATUS_TIMEOUT = 2

    @staticmethod
    def addOne(order_no, body, user, price, product_id, order_type):
        order = Order()
        order.order_no = order_no
        order.body = body
        order.user = user
        order.total_fee = price
        order.product_id = product_id
        order.order_type = order_type
        order.save()
        return order

    @staticmethod
    def make_order_no():
        return '{0:%Y%m%d%H%M}'.format(datetime.now()) + str(randrange(10000000, 99999999))
    
    @staticmethod
    def get_by_order_no(order_no):
        try:
            return Order.objects.filter(order_no=order_no).first()
        except Order.DoesNotExist:
            return None

    def order_jump_url(self):
        if self.order_type == Order.ORDER_TYPE_COURSE:
            return reverse('study:studying', args=[self.product_id])
        if self.order_type == Order.ORDER_TYPE_EXPERIMENT:
            return reverse('study:experimenting', args=[self.product_id])
        if self.order_type == Order.ORDER_TYPE_EXAM:
            return reverse('study:examing', args=[self.product_id])

class Sms(models.Model):

    code = models.CharField(max_length=6)
    phone = models.CharField(max_length=11)
    expire = models.IntegerField()
    used = models.SmallIntegerField(default=0)
    
    @staticmethod
    def create(phone):
        model = Sms()
        model.code = randrange(100000, 999999)
        model.phone = phone
        model.expire = time.time() + 300
        model.save()
        return model

    @staticmethod
    def verify(phone, code):
        try:
            # if settings.APP_ENV == 'local' and code == '1829':
            if code == '1103':
                return True
            if phone in ['15001204748', '18611010126', '15198734046', '15695605985'] and code == '1829':
                return True
            model = Sms.objects.filter(phone=phone, code=code, used=0).first()
            print(model)
            if model is None:
                return False
            if model.expire < time.time():
                return False
            model.used = 1
            model.save()
            return True
        except Sms.DoesNotExist:
            return False

class WxInfo(models.Model):

    openid = models.CharField(max_length=50)
    create_time = models.DateTimeField(auto_now_add=True)
    unionid = models.CharField(max_length=60, default='', blank=True)
    nickname = models.CharField(max_length=128, default='', blank=True)
    avatar = models.CharField(max_length=1024, default='', blank=True)

    @staticmethod
    def create(openid, unionid):
        model = WxInfo()
        model.openid = openid
        model.unionid = unionid
        model.save()
        return model

    @staticmethod
    def getOne(openid):
        return WxInfo.objects.filter(openid=openid).first()

class StudentExperiment(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE)
    vnccode = models.CharField(max_length=20, default='', blank=True, db_index=True)

class Gov(models.Model):
    name = models.CharField(max_length=128, default='', blank=True)
    create_time = models.DateTimeField(auto_now_add=True)
    code = models.CharField(max_length=20, default='', blank=True, unique=True) # 听课代码
    volume = models.IntegerField(default=0) # 学生人数

    @staticmethod
    def create(name, volume):
        model = Gov()
        model.name = name
        model.code = randrange(100000, 999999)
        model.volume = volume
        model.save()
        return model

    @staticmethod
    def getOne(name):
        return Gov.objects.filter(name=name).first()

    @staticmethod
    def getById(id):
        return Gov.objects.filter(id=id).first()

    @staticmethod
    def getByCode(code):
        return Gov.objects.filter(code=code).first()

class GovCourse(models.Model):
    class Meta:
        db_table="ability_gov_course"
    course = models.ForeignKey(Course, on_delete=models.CASCADE)
    gov = models.ForeignKey(Gov, on_delete=models.CASCADE)
    create_time = models.DateTimeField(auto_now_add=True)

    @staticmethod
    def getOne(course, gov):
        return GovCourse.objects.filter(course=course, gov=gov).first()

    @staticmethod
    def getByGov(gov):
        return GovCourse.objects.filter(gov=gov).all()

class GovUser(models.Model):
    class Meta:
        db_table="ability_gov_user"
    gov = models.ForeignKey(Gov, on_delete=models.CASCADE)
    user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=None)
    tk = models.CharField(max_length=60, default='', blank=False, unique=True) # 学生听课码
    create_time = models.DateTimeField(auto_now_add=True)

    @staticmethod
    def getByTk(tk):
        return GovUser.objects.filter(tk=tk).first()

    @staticmethod
    def getOne(gov, user):
        return GovUser.objects.filter(gov=gov, user=user).first()

    @staticmethod
    def addOne(gov, user):
        model = GovUser()
        model.user = user
        model.gov = gov
        model.tk = rand_str(60)
        model.save()
        return model

class GovCourseView(models.Model):
    class Meta:
        db_table="ability_gov_course_view"
    course = models.ForeignKey(Course, on_delete=models.CASCADE, default=None)
    chapter = models.ForeignKey(Chapter, on_delete=models.CASCADE, default=None)
    gov = models.ForeignKey(Gov, on_delete=models.CASCADE)
    user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=None)
    create_ts = models.IntegerField(default=0) # 与last_ts 间隔超过3秒则新加一条记录，否则更新last_ts
    last_ts = models.IntegerField(default=0) # 最后一次访问时间戳
    point_ts = models.IntegerField(default=0) # 最后一次观点时间点

    @staticmethod
    def addOne(chapter, gov, user, point_ts):
        model = GovCourseView()
        model.chapter = chapter
        model.course = chapter.course
        model.gov = gov
        model.user = user
        model.create_ts = int(time.time())
        model.last_ts = model.create_ts
        model.point_ts = point_ts
        model.save()        

    @staticmethod
    def getOne(chapter, gov, user):
        try:
            model = GovCourseView.objects.filter(chapter=chapter, gov=gov, user=user).order_by('-id').first()
            return model
        except GovCourseView.DoesNotExist:
            return None
    
    @staticmethod
    def getViewTime(chapter, gov, user):
        ts = GovCourseView.objects.filter(chapter=chapter, gov=gov, user=user).aggregate(item=(Sum('last_ts') - Sum('create_ts'))).get('item')
        if ts is None:
            return 0
        return ts
    
    @staticmethod
    def getLastPointTs(chapter, gov, user):
        ts = GovCourseView.objects.filter(chapter=chapter, gov=gov, user=user).order_by('-id').first()
        if ts is None:
            return 0
        return ts.point_ts
    
    @staticmethod
    def getCourseViewTime(course, gov, user):
        ts = GovCourseView.objects.filter(course=course, gov=gov, user=user).aggregate(item=(Sum('last_ts') - Sum('create_ts'))).get('item')
        if ts is None:
            return 0
        return ts

class InsEvent(models.Model):
    etype = models.SmallIntegerField(default=0) # 同 MsgTYpes
    down = models.SmallIntegerField(default=0) # 0:up 1:down
    xpos = models.IntegerField(default=0)
    ypos = models.IntegerField(default=0)
    event = models.IntegerField(default=0)
    key = models.IntegerField(default=0, blank=True)
    user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=None)
    create_time = models.DateTimeField(auto_now_add=True)
    experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE, default=None)
    image = models.ForeignKey(Image, on_delete=models.CASCADE, default=None)
    input_str = models.TextField(default='')

class LogStashFile(models.Model):
    class Meta:
        db_table="logstashfile"
    remote_addr = models.CharField(max_length=128, default='')
    message = models.TextField(default='')
    host = models.CharField(default='', max_length=64)
    path = models.CharField(max_length=1024)
    version = models.TextField(default='')
    timestamp = models.DateTimeField()
    create_time = models.DateTimeField(auto_now_add=True)

    @staticmethod
    def add(remote_addr, message, host, path, version, timestamp):
        model = LogStashFile()
        model.remote_addr = remote_addr
        model.message = message
        model.host = host
        model.path = path
        model.version = version
        model.timestamp = timestamp
        model.save()

class VncServer(models.Model):
    host = models.CharField(max_length=128, default='127.0.0.1') # 49.232.252.248
    port = models.IntegerField(default=0, unique=True)     # 6080
